﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Freedom.Common.Collections
{
    /// <summary>
    /// CollectionChangesCollector
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CollectionChangesCollector<T> : IDisposable
    {
        private readonly Dictionary<T, CollectionChangedAction> _items = new Dictionary<T, CollectionChangedAction>();
        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

        private bool _needRefresh;

        /// <summary>
        /// Gets a value indicating whether this instance is empty.
        /// </summary>
        /// <value><c>true</c> if this instance is empty; otherwise, <c>false</c>.</value>
        public bool IsEmpty
        {
            get
            {
                if (!_lock.TryEnterReadLock(100)) return true;

                try
                {
                    return _needRefresh == false && _items.Count == 0;
                }
                finally
                {
                    _lock.ExitReadLock();
                }
            }
        }


        /// <summary>
        /// Puts the specified item.
        /// </summary>
        /// <param name="action">The action.</param>
        /// <param name="items">The items.</param>
        public void Put(CollectionChangedAction action, T[] items)
        {
            _lock.EnterWriteLock();
            try
            {
                if (_needRefresh) return;
                if (action == CollectionChangedAction.Reset)
                {
                    _needRefresh = true;
                    _items.Clear();
                }
                else
                {
                    SetAction(items, action);
                }
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Exctracts all.
        /// </summary>
        /// <returns></returns>
        public CollectionChanges<T> ExctractAll()
        {
            if (!_lock.TryEnterWriteLock(100)) return null;

            try
            {

                if (_needRefresh)
                {
                    _needRefresh = false;
                    return new CollectionChanges<T>();
                }

                if (_items.Count == 0) return null;

                var toRemove = new List<T>();
                var toAdd = new List<T>();
                var toUpdate = new List<T>();
                var toInsert = new List<T>();

                var result = new CollectionChanges<T>(toAdd, toUpdate, toRemove, toInsert);

                foreach (var item in _items)
                {
                    switch (item.Value)
                    {
                        case CollectionChangedAction.Add:
                            toAdd.Add(item.Key);
                            break;
                        case CollectionChangedAction.Update:
                            toUpdate.Add(item.Key);
                            break;
                        case CollectionChangedAction.Remove:
                            toRemove.Add(item.Key);
                            break;
                        case CollectionChangedAction.InsertToTop:
                            toInsert.Add(item.Key);
                            break;
                        default:
                            break;
                    }
                }
                _items.Clear();
                return result;
            }
            finally
            {
                _lock.ExitWriteLock();
            }

        }

        /// <summary>
        /// Changes the action.
        /// </summary>
        /// <param name="items">The items.</param>
        public void ChangeAction(T[] items)
        {
            if (!_lock.TryEnterWriteLock(100)) return ;

            if (items == null) return;

            try
            {

                if (_needRefresh)
                {
                    _needRefresh = false;
                    return ;
                }

                if (_items.Count == 0) return ;

                foreach (var item in items)
                {
                    if (_items.ContainsKey(item))
                        if (_items[item] == CollectionChangedAction.Add)
                            _items[item] = CollectionChangedAction.Update;
                }
            }
            finally
            {
                _lock.ExitWriteLock();
            }

        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            try
            {
                _lock.Dispose();
            }
            catch 
            {
            }
        }

        private void SetAction(IEnumerable<T> items, CollectionChangedAction action)
        {
            foreach (var item in items)
            {
                _items[item] = action;
            }
        }

    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CollectionChanges<T>
    {
        /// <summary>
        /// Gets or sets a value indicating whether [need refresh].
        /// </summary>
        /// <value><c>true</c> if [need refresh]; otherwise, <c>false</c>.</value>
        public bool NeedRefresh { get; private set; }

        /// <summary>
        /// Gets or sets to remove.
        /// </summary>
        /// <value>To remove.</value>
        public List<T> Remove { get; private set; }
        /// <summary>
        /// Gets or sets to update.
        /// </summary>
        /// <value>To update.</value>
        public List<T> Update { get; private set; }
        /// <summary>
        /// Gets or sets to add.
        /// </summary>
        /// <value>To add.</value>
        public List<T> Add { get; private set; }

        /// <summary>
        /// Gets the insert to top.
        /// </summary>
        public List<T> InsertToTop { get; private set; }


        /// <summary>
        /// Initializes a new instance of the <see cref="CollectionChanges&lt;T&gt;"/> class.
        /// </summary>
        internal CollectionChanges()
        {
            NeedRefresh = true;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="CollectionChanges&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="add">The add.</param>
        /// <param name="update">The update.</param>
        /// <param name="delete">The delete.</param>
        /// <param name="insertToTop">The insert to top.</param>
        internal CollectionChanges(List<T> add, List<T> update, List<T> delete, List<T> insertToTop)
        {
            NeedRefresh = false;
            Add = add;
            Update = update;
            Remove = delete;
            InsertToTop = insertToTop;
        }
    }
}
